#include <config.h>
#include <core.h>


static msgd_uint_t   nthreads;
static msgd_uint_t   max_threads;
static msgd_uint_t   msgd_threaded = 0;


static pthread_attr_t  thr_attr;


msgd_int_t
msgd_create_thread(pthread_t *tid, msgd_thread_value_t (*func)(void *arg),
    void *arg)
{
    int  err;

    err = pthread_create(tid, &thr_attr, func, arg);

    if (err != 0) {
        ERROR("pthread_create() failed");
        return MSGD_ERROR;
    }

    DEBUG("thread is created: %u", *tid);

    nthreads++;

    return MSGD_OK;
}


msgd_int_t
msgd_init_threads(int n, size_t size)
{
    int  err;

    max_threads = n;

    err = pthread_attr_init(&thr_attr);

    if (err != 0) {
        ERROR("pthread_attr_init() failed");
        return MSGD_ERROR;
    }

    err = pthread_attr_setstacksize(&thr_attr, size);

    if (err != 0) {
        ERROR("pthread_attr_setstacksize() failed");
        return MSGD_ERROR;
    }

    msgd_threaded = 1;

    return MSGD_OK;
}

msgd_mutex_t *
msgd_mutex_init()
{
    int           err;
    msgd_mutex_t  *m;

    m = malloc(sizeof(msgd_mutex_t));
    if (m == NULL) {
        return NULL;
    }

    err = pthread_mutex_init(m, NULL);

    if (err != 0) {
        ERROR("pthread_mutex_init() failed");
        return NULL;
    }

    return m;
}


void
msgd_mutex_destroy(msgd_mutex_t *m)
{
    int  err;

    err = pthread_mutex_destroy(m);

    if (err != 0) {
        ERROR("pthread_mutex_destroy(%p) failed", m);
    }

    free(m);
}


void
msgd_mutex_lock(msgd_mutex_t *m)
{
    int  err;

    if (!msgd_threaded) {
        return;
    }

    DEBUG("lock mutex %p", m);

    err = pthread_mutex_lock(m);

    if (err != 0) {
        ERROR("pthread_mutex_lock(%p) failed", m);
        abort();
    }

    DEBUG("mutex %p is locked", m);

    return;
}


msgd_int_t
msgd_mutex_trylock(msgd_mutex_t *m)
{
    int  err;

    if (!msgd_threaded) {
        return MSGD_OK;
    }

    DEBUG("try lock mutex %p", m);

    err = pthread_mutex_trylock(m);

    if (err == EBUSY) {
        return MSGD_AGAIN;
    }

    if (err != 0) {
        ERROR("pthread_mutex_trylock(%p) failed", m);
        abort();
    }

    DEBUG("mutex %p is locked", m);

    return MSGD_OK;
}


void
msgd_mutex_unlock(msgd_mutex_t *m)
{
    int  err;

    if (!msgd_threaded) {
        return;
    }

    DEBUG("unlock mutex %p", m);

    err = pthread_mutex_unlock(m);

    if (err != 0) {
        ERROR("pthread_mutex_unlock(%p) failed", m);
        abort();
    }

    DEBUG("mutex %p is unlocked", m);

    return;
}


msgd_cond_t *
msgd_cond_init()
{
    int          err;
    msgd_cond_t  *cv;

    cv = malloc(sizeof(msgd_cond_t));
    if (cv == NULL) {
        return NULL;
    }

    err = pthread_cond_init(cv, NULL);

    if (err != 0) {
        ERROR("pthread_cond_init() failed");
        return NULL;
    }

    return cv;
}


void
msgd_cond_destroy(msgd_cond_t *cv)
{
    int  err;

    err = pthread_cond_destroy(cv);

    if (err != 0) {
       ERROR("pthread_cond_destroy(%p) failed", cv);
    }

    free(cv);
}


msgd_int_t
msgd_cond_wait(msgd_cond_t *cv, msgd_mutex_t *m)
{
    int  err;

    DEBUG("cv %p wait", cv);

    err = pthread_cond_wait(cv,m);

    if (err != 0) {
        ERROR("pthread_cond_wait(%p) failed", cv);
        return MSGD_ERROR;
    }

    ERROR("cv %p is waked up", cv);

    ERROR("mutex %p is locked", m);

    return MSGD_OK;
}


msgd_int_t
msgd_cond_signal(msgd_cond_t *cv)
{
    int  err;

    ERROR("cv %p to signal", cv);

    err = pthread_cond_signal(cv);

    if (err != 0) {
        ERROR("pthread_cond_signal(%p) failed", cv);
        return MSGD_ERROR;
    }

    ERROR("cv %p is signaled", cv);

    return MSGD_OK;
}
